//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
/*
 * Generic receive layer for the PXA USB client function
 *
 * This code was loosely inspired by the original version which was
 * Copyright (c) Compaq Computer Corporation, 1998-1999
 * Copyright (c) 2001 by Nicolas Pitre
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 * 02-May-2002
 *   Frank Becker (Intrinsyc) - derived from sa1100 usb_recv.c
 * 
 * TODO: Add support for DMA.
 *
 */

#ifdef HAVE_CONFIG_H
# include <blob/config.h>
#endif

#if 0
#include <blob/arch.h>
#include <blob/types.h>
#include <blob/init.h>
#endif

#include <linux/errno.h>
#include "pxa_usb.h"
#include "bvd_usb_ctl.h"
#include "bulverde.h"
#include "types.h"
#include "util.h"

#undef DEBUG
//#define DEBUG 3
#if DEBUG
static unsigned int usb_debug = DEBUG;
#else
#define usb_debug 0     /* gcc will remove all the debug code for us */
#endif

static char *ep2_buf;
static int   ep2_len;
static int   ep2_remain;
static usb_callback_t ep2_callback;
static int rx_pktsize;

void
ep2_start(void)
{
    /* disable DMA, RPC & SST should not be cleared */
    /* UDCCS2 &= ~( UDCCS_BO_DME | UDCCS_BO_RPC | UDCCS_BO_SST ); */

    /* enable interrupts for endpoint 2 (bulk out) */
    /* UICR0 &= ~UICR0_IM2; */
    UDCICR0 = 0x3f;
}

void
ep2_done(int flag)
{
    int size = ep2_len - ep2_remain;

    if (!ep2_len)
        return;

    ep2_len = 0;
    if (ep2_callback) {
        ep2_callback(flag, size);
    }
}

void
ep2_state_change_notify(int new_state)
{
}

void
ep2_stall(void)
{
    /* SET_FEATURE force stall at UDC */
    /* UDCCS2 |= UDCCS_BO_FST; */
}

int
ep2_init(int chn)
{
    /*
    int i;
    desc_t * pdesc = pxa_usb_get_descriptor_ptr();

    for ( i = 0; i < pdesc->ep_num; i++ ) {
        if( BULK_OUT1 == ( 0xF & pdesc->ep[i].bEndpointAddress) ) {
            rx_pktsize = __le16_to_cpu( pdesc->ep[i].wMaxPacketSize );
        }
    }
    */

    /* FIXME */
    rx_pktsize = 64;
    ep2_done(-EAGAIN);

    return 0;
}

void
ep2_reset(void)
{
    /*
    int i;
    desc_t * pdesc = pxa_usb_get_descriptor_ptr();

    for ( i = 0; i < pdesc->ep_num; i++ ) {
        if( BULK_OUT1 == ( 0xF & pdesc->ep[i].bEndpointAddress) ) {
            rx_pktsize = __le16_to_cpu( pdesc->ep[i].wMaxPacketSize );
        }
    }
    */
    /* FIXME */
    rx_pktsize = 64;
    /* UDCCS2 &= ~UDCCS_BO_FST; */
    ep2_done(-EINTR);
}

void
ep2_int_hndlr(int udcsr)
{
    int status = UDCCSRB;
    if( usb_debug) printk("ep2_int_hndlr: UDCCS2=%x\n", status);

    if ((status & (UDCCSR_PC | UDCCSR_SP)) == UDCCSR_SP) {
        /* zero-length packet */
        if (usb_debug)
            printk("%s:zero-length packet\n", __FUNCTION__);
        UDCCSRB |= UDCCSR_PC;
    }

    if (status & UDCCSR_PC) {
        int len, ignlen;
        int i;
        __u8 *buf = ep2_buf + ep2_len - ep2_remain;

        /* bytes in FIFO */
        len = (UDCBCRB & 0x3ff);

        if (usb_debug)
            printk("usb_recv: len=%d out1_len=%d out1_remain=%d\n",
                            len,ep2_len,ep2_remain);

        if (len > ep2_remain) {
            ignlen = len - ep2_remain;
            /* FIXME: if this happens, we need a temporary overflow buffer */
            if (usb_debug)
                printk("usb_recv: Buffer overwrite warning...\n");
            len = ep2_remain;
        }

        /* read data out of fifo */
        for (i = 0; i < len; i += 4) {
            *((__u32*)buf) = UDCDRB;
            if (usb_debug > 2)
                printk("-> %02x %02x %02x %02x\n", buf[0], buf[1], buf[2],
                                buf[3]);
            buf += 4;
        }

        /* emtpy fifo, here */
        while (UDCCSRB & UDCCSR_BNE) {
            __u32 data = UDCDRB;
        }

        /* ack RPC - FIXME: '|=' we may ack SST here, too */
        UDCCSRB |= UDCCSR_PC;

        ep2_remain -= len;
        ep2_done((len) ? 0 : -EPIPE);
    }

    return;
}

int
ep2_recv(char *buf, int len, usb_callback_t callback)
{
/* Nam9, 2004. 9. 3 */
// int flags;

/* Alek's patch */
// if (ep2_len)
//  return -EBUSY;

    ep2_buf = buf;
    ep2_len = len;
    ep2_callback = callback;
    ep2_remain = len;
    ep2_start();

    return 0;
}
